package hyl.base.medium.Filter;

import static org.bytedeco.ffmpeg.global.avcodec.*;
import static org.bytedeco.ffmpeg.global.avformat.*;
import static org.bytedeco.ffmpeg.global.avutil.*;
import java.io.IOException;
import java.io.OutputStream;
import org.bytedeco.ffmpeg.avcodec.AVCodecContext;
import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVIOContext;
import org.bytedeco.ffmpeg.avformat.AVOutputFormat;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.avutil.AVRational;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.PointerPointer;
import org.bytedeco.javacv.FrameRecorder.Exception;

import hyl.core.MyFun;
import hyl.core.run.IFilter;

public class RtmpFilter implements IFilter<AVPacket> {

///////////////////输出/////////
//输出引擎
	AVFormatContext 输出引擎 = null;
	AVFormatContext 输入引擎 = null;
	// 编码器
	AVCodecContext 视频编码器 = null;
	AVCodecContext 音频编码器 = null;
	AVRational av帧率 = null; // 以视频帧为基准

	AVStream 输入流;

	OutputStream 输出分流 = null;
	AVOutputFormat oformat = null;
	boolean 是否帧交错 = true;

	public RtmpFilter(String 输出路径, AVFormatContext 输入引擎) {

		_url = 输出路径;
		this.输入引擎 = 输入引擎;
		if (MyFun.isEmpty(_url)) {
			System.out.println("路径不能为空");
			return;
		}
		try {
			step1();
		} catch (IOException e) {
			e.printStackTrace();
			release();
		}
	}



	String _url;
	String 输出格式名 = "flv";

	String step1() throws IOException {
		if (_url.startsWith("rtmp://"))
			输出格式名 = "flv";
		else if (_url.startsWith("udp://"))
			输出格式名 = "mpegts";
		else
			return "输出格式不对";

		输出引擎 = new AVFormatContext(null);
		//avformat_alloc_context2();
		if (avformat_alloc_output_context2(输出引擎, null, 输出格式名, _url) < 0) {
			throw new Exception("avformat_alloc_context2() error:\t无法创建媒体上下文");
		}
		oformat = 输出引擎.oformat();
//		if ((输出引擎.flags() & AVFMT_GLOBALHEADER) == AVFMT_GLOBALHEADER) {
//			输出引擎.flags(输出引擎.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
//
//		}
		// Dump Format------------------
		av_dump_format(输出引擎, 0, _url, 1);
		// 打开输出URL（Open output URL）

		int ret = 0;
		// 从输入设备 分流出视频流和音频流
		for (int idx = 0; idx < 输入引擎.nb_streams(); idx++) {
			AVStream inputStream = 输入引擎.streams(idx);
			AVCodecParameters in_codecpar = inputStream.codecpar();
			if (in_codecpar.codec_type() != AVMEDIA_TYPE_AUDIO && in_codecpar.codec_type() != AVMEDIA_TYPE_VIDEO
					&& in_codecpar.codec_type() != AVMEDIA_TYPE_SUBTITLE) {
				continue;
			}

			// 2.2 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
			AVStream 输出流 = avformat_new_stream(输出引擎, null);
			if (null == 输出流) {
				System.out.println("avformat_new_stream() 遇到异常,无法实例输出流\n");
				continue;
			}

			// 2.3 将当前输入流中的参数拷贝到输出流中
			ret = avcodec_parameters_copy(输出流.codecpar(), in_codecpar);
			if (ret < 0) {
				System.out.println("avcodec_parameters_copy()  遇到异常, 无法拷贝参数\n");
				continue;
			}
			输出流.codecpar().codec_tag(0);
		}

		if ((oformat.flags() & AVFMT_NOFILE) == 0) {
			AVIOContext pb = new AVIOContext(null);
			if ((ret = avio_open(pb, _url, AVIO_FLAG_WRITE)) < 0) {
				return "avio_open() 无法开打输出文件";
			}
			输出引擎.pb(pb);
			System.out.println("打开数据文件");
		}
		av_dump_format(输出引擎, 0, _url, 1);
		// AVDictionary options = new AVDictionary(null);
		// 写文件头（Write file header）写入空的头文件
		ret = avformat_write_header(输出引擎, (PointerPointer<Pointer>) null);
		if (ret < 0) {
			throw new Exception("avformat_write_header() error:\t无法写入头文件");
		}
		// pkt=new AVPacket();
		开始时间 = av_gettime();

		return null;
	}

	Long 开始时间 = 0L;

//
//	void 同步(  AVStream 输入流,AVStream 输出流,int idx,AVPacket avPacket  ){	
//		av_packet_rescale_ts(avPacket, 输入流.time_base(), 输出流.time_base());
//		avPacket.stream_index(idx);
//		avPacket.flags(avPacket.flags() | AV_PKT_FLAG_KEY);
//	}

	// 过滤包
	@Override
	/**
	 * return 为true 终断遍历
	 */
	public boolean where(AVPacket avPacket) {

		// 直接输出不改变任何packet配置 包括timebase pts dts 和duration
		if (isover) {
			return false;
		}
		// System.out.println("包过滤1_1");
		AVStream in_stream, out_stream;
		int idx = avPacket.stream_index();
		// System.out.println("-2-");
		in_stream = 输入引擎.streams(idx);
		out_stream = 输出引擎.streams(idx);
		av_packet_rescale_ts(avPacket, in_stream.time_base(), out_stream.time_base());
		// System.out.println("包过滤1_2");
		avPacket.pos(-1);
		int ret = 0;
		if (是否帧交错) {
			ret = av_interleaved_write_frame(输出引擎, avPacket);
		} else {
			ret = av_write_frame(输出引擎, avPacket);
		}
		// System.out.println("包过滤1_3");
		if (ret < 0) {
			System.out.println("av_interleaved_write_frame 异常\n");
			return true;
		}

		return false;
	}

	boolean isover = false;

	public void close() {
		isover = true;

		/* 写入尾部 */
		av_write_trailer(输出引擎);
		release();
	}

	void release() {
		if (视频编码器 != null) {
			avcodec_free_context(视频编码器);
			avcodec_close(视频编码器);// Close the codec
		}
		if (音频编码器 != null) {
			avcodec_free_context(音频编码器);
			avcodec_close(音频编码器);// Close the codec
		}
		if (输出引擎 != null)
			// 关闭图像源
			avformat_close_input(输出引擎);// Close the video file
	}
}

// 输出格式,输出文件
//public void 包输出(String format_name, String filename) throws Exception {

//
//
//	
//	 
//		
//		ofmt = oc.oformat();
//		video_c=out_stream.codec();
//		for (i = 0; i < ifmt_ctx.nb_streams(); i++) {
//			//根据输入流创建输出流（Create output AVStream according to input AVStream）
//			AVStream in_stream = ifmt_ctx.streams(i);
//			AVStream out_stream = avformat_new_stream(oc, in_stream.codec().codec());
//			
//			//复制AVCodecContext的设置（Copy the settings of AVCodecContext）
//			ret = avcodec_copy_context(out_stream.codec(), in_stream.codec());
//			if (ret < 0) {
//				System.out.println("无法复制上下文");
//				end();
//				return;
//		
//			}
//			
//			  if (ofmt.flags() & AVFMT_GLOBALHEADER) != 0) {
//				  out_stream.codec().flags(out_stream.codec().flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
//	            }
//			  if ((oformat.flags() & AVFMT_GLOBALHEADER) != 0) {
//		            video_c.flags(video_c.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
//		        }
//		}

//		while (true) {
//			AVStream in_stream, out_stream;
//			//获取一个AVPacket（Get an AVPacket）
//			ret = av_read_frame(ifmt_ctx, &pkt);
//			if (ret < 0)
//				break;
//			//FIX：No PTS (Example: Raw H.264)
//			//Simple Write PTS
//			if(pkt.pts==AV_NOPTS_VALUE){
//				//Write PTS
//				AVRational time_base1=ifmt_ctx.streams[videoindex].time_base;
//				//Duration between 2 frames (us)
//				int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx.streams[videoindex].r_frame_rate);
//				//Parameters
//				pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
//				pkt.dts=pkt.pts;
//				pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
//			}
//			//Important:Delay
//			if(pkt.stream_index==videoindex){
//				AVRational time_base=ifmt_ctx.streams[videoindex].time_base;
//				AVRational time_base_q={1,AV_TIME_BASE};
//				int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
//				int64_t now_time = av_gettime() - start_time;
//				if (pts_time > now_time)
//					av_usleep(pts_time - now_time);
//	 
//			}
//	 
//			in_stream  = ifmt_ctx.streams[pkt.stream_index];
//			out_stream = oc.streams[pkt.stream_index];

//			//Print to Screen
//			if(pkt.stream_index==videoindex){
//				printf("Send %8d video frames to output URL\n",frame_index);
//				frame_index++;
//			}
//			//ret = av_write_frame(oc, &pkt);
//			ret = av_interleaved_write_frame(oc, &pkt);
//	 
//			if (ret < 0) {
//				printf( "Error muxing packet\n");
//				break;
//			}
//			
//			av_free_packet(&pkt);
//			
//		}
//		//写文件尾（Write file trailer）
//		av_write_trailer(oc);
//	
//	    //有些格式希望流标题是分开的
//
//
//	avformat_close_input(&ifmt_ctx);
//	/* close output */
//	if (oc && !(ofmt.flags & AVFMT_NOFILE))
//		avio_close(oc.pb);
//	avformat_free_context(oc);
//	if (ret < 0 && ret != AVERROR_EOF) {
//		printf( "Error occurred.\n");
//		return -1;
//	}
//	return 0;
//}